#pragma once

#include "Calls.h"

Calls::Calls(VarTable* vt,StmtTable *st, ProcTable* pt)
{
	myST = st;
	myVT = vt;
	myPT = pt;
};

void Calls::set(INDEX procNo1, INDEX procNo2) 
{
	callsTableStmt.insert(std::make_pair<INDEX,INDEX>(procNo1,procNo2));
	++count;
};

void Calls::computeClosure()
{
	pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
	unordered_set<INDEX> accum;
	bool isChanged;
	int initSize;

	for(int i = 0;i<=myPT->getSize();i++)
	{
		isChanged = true;
		accum.clear();

		pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
		ret = callsTableStmt.equal_range(i);
		for (unordered_multimap<INDEX,INDEX>::iterator it=ret.first; it!=ret.second; ++it)
		{
			accum.insert(it->second);
		}
		while(isChanged)
		{
			isChanged = false;
			initSize = accum.size();
			
			for (auto it = accum.begin(); it != accum.end(); ++it)
			{
				pair <unordered_multimap<INDEX,INDEX>::iterator, unordered_multimap<INDEX,INDEX>::iterator> ret;
				ret = callsTableStmt.equal_range(*it);
				for (unordered_multimap<INDEX,INDEX>::iterator it=ret.first; it!=ret.second; ++it)
				{
					accum.insert(it->second);
				}
			}
			if(initSize!=accum.size())
			{
				isChanged = true;
			}
		}
		for (auto it = accum.begin(); it != accum.end(); ++it)
		{
			callsTableStmtClosure.insert((std::make_pair<INDEX,INDEX>(i,*it)));
		}
	}
};

Schema Calls::getSynSyn(NAME de1Name,NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1Name!=de2Name)
	{
		for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
		}
		return x;
	}
	else
	{
		return x;
	}
};

Schema Calls::getSynIdent(NAME de1Name, string para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
		if(it->second==callingProcIndex)
		{
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getSynUnd(NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
	}
	return x;
};

Schema Calls::getIdentSyn(string para1, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
		if(it->first==callerProcIndex)
		{
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getIdentIdent(string para1, string para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
		if ((it->first==callerProcIndex) && (it->second==callingProcIndex)) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getIdentUnd(string para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
		if (it->first==callerProcIndex) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getUndIdent(string para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
		if ((it->second==callingProcIndex)) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getUndSyn(NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
	}
	return x;
};

Schema Calls::getUndUnd()
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = callsTableStmt.begin(); it != callsTableStmt.end(); ++it) 
	{
		vector<INDEX> vect;
		vect.push_back(it->second);
		x.insertTuple(vect);
	}
	return x;
};

Schema Calls::getSynSynClosure(NAME de1Name,NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1Name!=de2Name)
	{
		for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
		}
		return x;
	}
	else
	{
		return x;
	}
};

Schema Calls::getSynIdentClosure(NAME de1Name, string para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
		if(it->second==callingProcIndex)
		{
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getSynUndClosure(NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
	}
	return x;
};

Schema Calls::getIdentSynClosure(string para1, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
		if(it->first==callerProcIndex)
		{
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getIdentIdentClosure(string para1, string para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
		if ((it->first==callerProcIndex) && (it->second==callingProcIndex)) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getIdentUndClosure(string para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callerProcIndex = myPT->getProcIndex(para1);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
		if (it->first==callerProcIndex) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getUndIdentClosure(string para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	INDEX callingProcIndex = myPT->getProcIndex(para2);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
		if ((it->second==callingProcIndex)) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema Calls::getUndSynClosure(NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
	}
	return x;
};

Schema Calls::getUndUndClosure()
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = callsTableStmtClosure.begin(); it != callsTableStmtClosure.end(); ++it) 
	{
		vector<INDEX> vect;
		vect.push_back(it->second);
		x.insertTuple(vect);
	}
	return x;
};